#define STRICT
#define WIN32_LEAN_AND_MEAN

#include <windows.h>
#include <assert.h>
#include <time.h>
#include "Hpps\Audio.hpp"
#include "resource1.h"
#include "Input.h"
#include "World.h"


#define SQUARED(x)		((x)*(x))
#define NUM_ELEMENTS(x)	(sizeof(x) / sizeof(x[0]))

// used for the game state machine
enum eGameState
{
	INIT,
	INTRO,
	MENU,
	GAME,
	CREDITS,
	QUIT
};

/*=======VARIABLES BEGIN*/
eGameState		g_GameState		= INIT;		// the state variable, defaulted to init
const int		cnScreenWidth	= 800;
const int		cnScreenHeight	= 600;

DrawList	drawlist;				//Vector of the game's characters

HWND hWnd;
/*=======VARIABLES END*/
/*=======PROTOTYPES BEGIN*/

bool Initialize(HINSTANCE hInstance, int nCmdShow);		// initializes all systems such as Audio and Graphics
void Update(HINSTANCE hInst, int nCmdShow);				// main program's update
void UpdateGame(DrawList& drawlist);					// updates the game world by one frame

LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
HWND OpenWindow(const char* cszWinClassName, const char* cszWindowName, int nCmdShow);
/*=======PROTOTYPES END*/

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{	
	MSG uMsg;
	memset(&uMsg, 0, sizeof(uMsg));



	while (uMsg.message != WM_QUIT && g_GameState != QUIT)
	{
		while (PeekMessage(&uMsg, NULL, 0, 0, PM_REMOVE))
		{
			TranslateMessage(&uMsg);
			DispatchMessage(&uMsg);
		}
		Update(hInstance, nCmdShow);
		DirectInput::Engine()->PollDevices();				// have Input class gather new input from devices

	}

	PostQuitMessage(0);

	UnregisterClass("D3DWindow", hInstance);

	return uMsg.wParam;
}

bool Initialize(HINSTANCE hInstance, int nCmdShow)
{
	hWnd = OpenWindow("D3DWindow", "Minos", nCmdShow);	// initialize the window
	if (NULL == hWnd)
		return false;

	if(!Graphics::Engine()->Init(hWnd))							// initialize the graphics class
		return false;
	else if(!DirectInput::Engine()->Init(hWnd, hInstance))		// initialize the input class
		return false;
	else if(!Audio::Engine()->Init())							// initialize the audio class
		return false;

	srand((unsigned)time(0));
	Maze::Engine()->CreateMap(0,0,drawlist);
	Maze::Engine()->SetListInfo(drawlist);

	//drawlist.pop_back();


	Graphics::Engine()->SetCameraPos(Maze::Engine()->GetCenterofMap());

	return true;												// return true if everything went better than expected
}
void Update(HINSTANCE hInst, int nCmdShow)
{
	switch(g_GameState)
	{
	case INIT:
		if(Initialize(hInst, nCmdShow))
		{
			Audio::Engine()->StartMusic();
			g_GameState = GAME;						// change this to intro once we have one
			break;
		}
		else
		{
			g_GameState = QUIT;						// if init failed, set game state to quit
			break;
		}
	case INTRO:
		break;
	case MENU:
		break;
	case GAME:
		if(!Graphics::Engine()->Render(drawlist))		// render one frame
			g_GameState = QUIT;
		else
		{
			UpdateGame(drawlist);
		}

		break;
	case CREDITS:
		break;
	case QUIT:
		break;
	}
}
void UpdateGame(DrawList& drawlist)
{
	Audio::Engine()->Update(DirectInput::Engine()->keyDown(DIK_SPACE));	// play effect if space is pressed
	Graphics::Engine()->Update();

	if(DirectInput::Engine()->keyDown(DIK_F5))		// generates a new random map
	{
		drawlist.clear();							// clear the old drawlist
		Maze::Engine()->Reset();					// clear the nodes of maze
		Maze::Engine()->CreateMap(0, 0, drawlist);	// create a new map
		Maze::Engine()->SetListInfo(drawlist);		// push that maze into the drawlist
	}

	for each(BaseObject* pObj in drawlist)
	{
		pObj->Update(drawlist);
	}
}

LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	switch(uMsg)
	{
	case WM_CLOSE:
		PostQuitMessage(0);
		break;
	case WM_KEYUP:
		switch(wParam)
		{
		case VK_ESCAPE:	// pressing escape closes the program because I'm too lazy to reach for my mouse - Stren
			PostQuitMessage(0);
			break;
		}

	}
	return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
HWND OpenWindow(const char* cszWinClassName, const char* cszWindowName, int nCmdShow)
{

	WNDCLASSEX winClass;
	hWnd = NULL;

	HINSTANCE hInstance = ::GetModuleHandle(NULL);
	assert(NULL != hInstance);
	HICON hIcon = ::LoadIcon(hInstance, MAKEINTRESOURCE(IDI_ICON1));
	//assert(NULL != hIcon);

	winClass.lpszClassName = cszWinClassName;
	winClass.cbSize        = sizeof(WNDCLASSEX);
	winClass.style         = CS_HREDRAW | CS_VREDRAW;
	winClass.lpfnWndProc   = WindowProc;
	winClass.hInstance     = hInstance;
	winClass.hIcon	   = hIcon;
	winClass.hIconSm	   = hIcon;
	winClass.hCursor       = ::LoadCursor(NULL, IDC_ARROW);
	winClass.hbrBackground = (HBRUSH)::GetStockObject(BLACK_BRUSH);
	winClass.lpszMenuName  = NULL;
	winClass.cbClsExtra    = 0;
	winClass.cbWndExtra    = 0;

	if (0 == RegisterClassEx(&winClass)){return NULL;}

	hWnd = ::CreateWindowEx(NULL,
		cszWinClassName,
		cszWindowName,
		WS_OVERLAPPEDWINDOW,
		0, 0,
		cnScreenWidth, cnScreenHeight,
		NULL, NULL, ::GetModuleHandle(NULL), NULL);

	ShowWindow(hWnd, nCmdShow);
	UpdateWindow(hWnd);
	return hWnd;
}